## GANET V2
## Copyright (c) 2014 Ganet Development Team. All rights reserved.
##
## This file is subject to the terms and conditions defined in file
## 'LICENSE.md', which is part of this source code package.
## See 'AUTHORS' file for a list of contributors.

import struct

STRING_TERMINATOR = ':'


class MessageEncoder(object):

    def __init__(self):
        self.reset()

    def reset(self):
        self._msg = ''
        
    def write_string(self, s):
        self._msg += "%s%s" % (s, STRING_TERMINATOR)

    def write_short(self, value):
        self._msg += struct.pack('!h', value)

    def write_int(self, value):
        self._msg += struct.pack('!i', value)

    def write_uint(self, value):
        self._msg += struct.pack('!I', value)

    def write_long(self, value):
        self._msg += struct.pack('!l', value)

    def write_float(self, value):
        self._msg += struct.pack('!f', value)

    def write_double(self, value):
        self._msg += struct.pack('!d', value)

    def write_byte(self, value):
        self._msg += struct.pack('!B', value)

    def write_bool(self, value):
        if value:
            v = 1
        else:
            v = 0
        self._msg += struct.pack('!B', value)

    def get_message(self):
        return str(self._msg)

class MessageDecoder(object):

    def __init__(self, msg = None):
        self.reset(msg)
        
    def _read(self, fmt):
        size = struct.calcsize(fmt)
        assert len(self._msg) - self._pos >= size, 'message out of data'
        result = struct.unpack(fmt, self._msg[self._pos:self._pos + size])
        self._pos += size
        return result[0]

    def reset(self, msg):
        if msg:
            self._msg = str(msg)
            self._pos = 0
        else:
            self._msg = ''
            self._pos = 0

    def get_remaining_message(self):
        return self._msg[self._pos:]
    
    def read_string(self):
        assert self._pos < len(self._msg)
        idx = self._msg.find(STRING_TERMINATOR, self._pos)

        if idx < 0:
            idx = len(self._msg)

        result = self._msg[self._pos:idx]
        self._pos += len(result) + 1
        return result

    def read_float(self):
        assert self._pos < len(self._msg)
        return self._read('!f')

    def read_double(self):
        assert self._pos < len(self._msg), 'message out of data'
        return self._read('!d')

    def read_byte(self):
        assert self._pos < len(self._msg), 'message out of data'
        return self._read('!B')

    def read_bool(self):
        assert self._pos < len(self._msg), 'message out of data'
        v = self._read('!B')
        if v:
            return True
        else:
            return False

    def read_short(self):
        assert self._pos < len(self._msg), 'message out of data'
        return self._read('!h')

    def read_int(self):
        assert self._pos < len(self._msg), 'message out of data'
        return self._read('!i')

    def read_uint(self):
        assert self._pos < len(self._msg), 'message out of data'
        return self._read('!I')

    def read_long(self):
        assert self._pos < len(self._msg), 'message out of data'
        return self._read('!l')


if __name__ == '__main__':
    encoder = MessageEncoder()
    encoder.write_byte(200)
    encoder.write_bool(True)
    encoder.write_string('foo was here')
    encoder.write_double(1234.5)
    msg = encoder.get_message()
    print
    
    decoder = MessageDecoder()
    decoder.reset(msg)
    print decoder.read_byte()
    print decoder.read_bool()
    print decoder.read_string()
    print decoder.read_double()
    
